﻿using FlexField.Metadata;
using Microsoft.Extensions.DependencyInjection.Extensions;

namespace Microsoft.Extensions.DependencyInjection
{
    /// <summary>
    /// Contains extension methods to <see cref="IServiceCollection"/> for configuring metadata services.
    /// </summary>
    public static class MetadataServiceCollectionExtensions
    {
        /// <summary>
        /// Adds and configures the metadata system.
        /// </summary>
        /// <param name="services">The services available in the application.</param>
        /// <returns>An <see cref="MetadataBuilder"/> for creating and configuring the metadata system.</returns>
        public static MetadataBuilder AddMetadataCore(this IServiceCollection services) => services.AddMetadataCore<string>();

        /// <summary>
        /// Adds and configures the metadata system.
        /// </summary>
        /// <param name="services">The services available in the application.</param>
        /// <returns>An <see cref="MetadataBuilder"/> for creating and configuring the metadata system.</returns>
        /// <typeparam name="TKey">The type used for the primary key for the fields and entities.</typeparam>
        public static MetadataBuilder AddMetadataCore<TKey>(this IServiceCollection services)
            where TKey : IEquatable<TKey>
            => services.AddMetadataCore<TKey>(_ => { });

        /// <summary>
        /// Adds and configures the metadata system.
        /// </summary>
        /// <param name="services">The services available in the application.</param>
        /// <param name="setupAction">An action to configure the <see cref="MetadataOptions"/>.</param>
        /// <returns>An <see cref="MetadataBuilder"/> for creating and configuring the metadata system.</returns>
        public static MetadataBuilder AddMetadataCore(this IServiceCollection services, Action<MetadataOptions> setupAction) => services.AddMetadataCore<string>(setupAction);

        /// <summary>
        /// Adds and configures the metadata system.
        /// </summary>
        /// <param name="services">The services available in the application.</param>
        /// <param name="setupAction">An action to configure the <see cref="MetadataOptions"/>.</param>
        /// <returns>An <see cref="MetadataBuilder"/> for creating and configuring the metadata system.</returns>
        /// <typeparam name="TKey">The type used for the primary key for the fields and entities.</typeparam>
        public static MetadataBuilder AddMetadataCore<TKey>(this IServiceCollection services, Action<MetadataOptions> setupAction)
            where TKey : IEquatable<TKey>
        {
            // Services depends on
            services.AddOptions().AddLogging();

            services.TryAddScoped<IEntityValidator<TKey>, EntityValidator<TKey>>();
            services.TryAddScoped<IFieldValidator<TKey>, FieldValidator<TKey>>();
            services.TryAddScoped<ILookupNormalizer, UpperInvariantLookupNormalizer>();
            services.TryAddScoped<MetadataErrorDescriber>();
            services.TryAddScoped<MetadataManager<TKey>>();

            if (setupAction != null)
            {
                services.Configure(setupAction);
            }

            return new MetadataBuilder(services, typeof(TKey));
        }
    }
}
